package org.bigjava.novelplatform.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.bigjava.novelplatform.vo.SearchBriefInfoVo;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 将entity转化为vo的工具类
 *
 * @author ykh
 * @create 2022-03-23 23:23
 */
@Slf4j
public class EntityTranslatorVo {
    /**
     * 将entity 转化为SearchBriefInfoVo
     *
     * @param entities 要转化的entity集合
     * @param id       entity对应SearchBriefInfoVo中id的字段
     * @param name     entity对应SearchBriefInfoVo中name的字段
     * @param Type     类型
     * @return
     */
    public static List<SearchBriefInfoVo> toSearchBriefInfoVo(List<?> entities, String id, String name, String Type) {
        ArrayList<SearchBriefInfoVo> vos = new ArrayList<>();
        for (Object entity : entities) {
            SearchBriefInfoVo vo = new SearchBriefInfoVo();
            Class<?> aClass = entity.getClass();
            String getIdMethod = "get" + id;
            String getNameMethod = "get" + name;

            Method[] methods = aClass.getMethods();
            Method getId = null;
            Method getName = null;
            for (int i = 0; i < methods.length; i++) {
                if (methods[i].getName().equalsIgnoreCase(getIdMethod)) {
                    getId = methods[i];
                } else if (methods[i].getName().equalsIgnoreCase(getNameMethod)) {
                    getName = methods[i];
                }
            }
            try {
                if (getId != null) {
                    Integer entityId = (Integer) getId.invoke(entity);
                    vo.setId(entityId);
                } else {
                    log.warn("警告：参数id无法匹配到get方法,请检查入参{}", id);
                }
                if (getName != null) {
                    String entityName = (String) getName.invoke(entity);
                    vo.setName(entityName);
                } else {
                    log.warn("警告：参数name无法匹配到get方法,请检查入参{}", name);
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
            vo.setType(Type);
            vos.add(vo);
        }
        return vos;
    }


    public static <T, C> T toVo(C entity, Class<T> voClass, String... mapFields){
        try {
            Field[] fields = voClass.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
            }
            //创建vo对象
            Constructor<T> voClassConstructor = voClass.getConstructor();
            T vo = voClassConstructor.newInstance();
            //获取有序的字段
            List<Field> voFieldList =
                    Arrays.stream(fields)
                            .filter(t -> !Objects.isNull(t.getAnnotation(FieldSort.class)
                            )).sorted(Comparator.comparingInt(t -> t.getAnnotation(FieldSort.class).value()))
                            .collect(Collectors.toList());
            if (voFieldList.size() < mapFields.length) {
                throw new RuntimeException("mapFields参数过多");
            }

            //获取有序字段的set方法
            List<Method> methodList = getPrefixMethods(voFieldList, voClass, "set");

            //获取entity的方法
            Class<?> itemClass = entity.getClass();
            ArrayList<Field> entityFieldList = new ArrayList<>();
            //获取实体类的指定字段
            for (String mapField : mapFields) {
                try {
                    Field mapEntityField = itemClass.getDeclaredField(mapField);
                    mapEntityField.setAccessible(true);
                    entityFieldList.add(mapEntityField);
                } catch (NoSuchFieldException | SecurityException e) {
                    log.warn("警告：无法从{}找到映射的字段{}",itemClass,mapField);
                }
            }
            List<Method> entityMethods = getPrefixMethods(entityFieldList, itemClass, "get");
            if(entityMethods.size() > methodList.size()){
                throw new RuntimeException("正确得到的实体类方法大于vo方法");
            }
            //转换
            for (int j = 0; j < Math.min(entityMethods.size(),methodList.size()); j++) {
                Object cast = entityMethods.get(j).invoke(entity);
                Method method = methodList.get(j);
                Class<?> parameterType = method.getParameterTypes()[0];
                //这里很有可能出现类型不匹配异常，需要小心
                if(parameterType == String.class){
                    method.invoke(vo,cast == null ? null : cast.toString() );
                }else {
                    method.invoke(vo,cast);
                }
            }
            return vo;
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException
                e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 将任意实体类型转化为voClass类型的vo，通过mapFields指定映射字段
     * 将实体字段mapFields[i]的值映射到vo含自定义注解FieldSort(i)的字段
     * 配合FieldSort注解，mapFields映射顺序要与FieldSort一致
     * mapFields需要与字段名大小写保持一致
     * @param entities  实体类
     * @param voClass   vo类型
     * @param mapFields 映射字段
     * @param <T>       vo类型
     * @param <C>       entity类型
     * @return
     */
    public static <T, C> List<T> toVos(List<C> entities, Class<T> voClass, String... mapFields) {
        return entities.stream().map((item) -> {
            return toVo(item,voClass,mapFields);
        }).collect(Collectors.toList());
    }

    /**
     * 获取所有voClass中get或set的方法
     *
     * @param fieldList
     * @param voClass
     * @param prefix get/set
     * @return
     */
    private static List<Method> getPrefixMethods(List<Field> fieldList, Class voClass, String prefix) {
        List<Method> methodList = fieldList.stream().map((t) -> {
            String MethodName = prefix + t.getName();
            byte[] bytes = MethodName.getBytes();
            bytes[prefix.length()] -= 32;
            MethodName = new String(bytes);
            Method method = null;
            String set = "set";
            String get = "get";
            try {
                if(set.equalsIgnoreCase(prefix)){
                    method = voClass.getMethod(MethodName, (Class) t.getType());
                }else if(get.equalsIgnoreCase(prefix) ){
                    try {
                        method = voClass.getMethod(MethodName);
                    } catch (NoSuchMethodException e) {
                        //尝试获取is方法
                        byte[] nameBytes = t.getName().getBytes();
                        nameBytes[0] -= 32;
                        String isMethodName = "is" + new String(nameBytes);
                        method = voClass.getMethod(isMethodName);
                    }
                }
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return method;
        }).collect(Collectors.toList());
        return methodList;
    }
}
